/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */

/*	Init
 *	Open
 *	Pre-request
 *	Request
 *	Response
 *	....
 *	Pre-request
 *	Request
 *	Response
 *	....
 *	Open
 *	...
 *	Clear
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/file.h>
#include "mycap.h"
#include "logger.h"
#include "helpers.h"
#include "init.h"
#include "headerhandler.h"
#include "deflate.h"



short readme(FILE** , const char* , const char*, char*);

#define logcap logd<<"CAPX: " 

/*************************************************************************
 * Connect  
 *************************************************************************/
CAP_RETURN MyCAP::ConnectCAP(void** param1, const char* host, char** ip, int* port){
	short retval;
	MyCapConnection* mycapcon = (MyCapConnection*)*param1;

	logd<<"CAP: open "<<host<<"("<<*ip<<")"<<":"<<*port<<lend;
	assert(*param1);

	if((mycapcon->request.connvalues & MYCAP_CON_BLOCK) && (mycapcon->request.connvalues & MYCAP_CON_HTTPS)){
		return CAP_REJECT;
	}
	else{
		return CAP_NOCHANGE;
	}
}

void MyCAP::CloseCAP(void** param1){
	MyCapConnection* mycapcon;

	if(*param1){
		MyCapConnection* mycapcon = (MyCapConnection*)*param1 ;
	
		if(mycapcon->request.url){
			free(mycapcon->request.url);
			mycapcon->request.url=0;	
		}
		free(*param1);
		*param1=0;
	}
}

/*************************************************************************
 * Pre-request
 *************************************************************************/
CAP_RETURN MyCAP::PreRequestCAP(void** param1,  HeaderHandlerInterface* hhi){
	//return CAP_NOCHANGE;
	short retval;
	char cmd;

	const char* ip;
	const char* url;
	const char* host;
	int urlsize;
	MyCapConnection* mycapcon;

	url	= hhi->GetRequestLine();
	host	= hhi->GetHeader("Host");
	ip	= hhi->GetHeader("X-Forwarded-For");

	logd<<"CAP: prerequest " << ip <<" | "<< host << " | "<<url<<lend;

	if(!*param1){
		mycapcon = (MyCapConnection*)malloc(sizeof(MyCapConnection));
		*param1 = mycapcon;
		mycapcon->request.url=0;
	}
	else{
		mycapcon = (MyCapConnection*)*param1;
	}
	mycapcon->request.connvalues=0;


	mycapcon->request.requeston=1;
		
	urlsize = strlen(url)+strlen(host)+1;

	if(mycapcon->request.url)
		free(mycapcon->request.url);	
	mycapcon->request.url = (char*)calloc(urlsize, 1);

	snprintf(mycapcon->request.url, urlsize, "%s %s", host, url);

	//findinlist(&hosts, host);
	//findinlist(&urls, url);

	//return CAP_NOCHANGE;	


	if(hhi->IsConnect())
		mycapcon->request.connvalues |= MYCAP_CON_HTTPS;

	switch(findinlist(&hosts, host)){
	case 0:
		mycapcon->request.connvalues |= MYCAP_CON_BLACK;
		break;
	case 1:
	default:
		switch(findinlist(&urls, url)){
		case 0:
			mycapcon->request.connvalues |= MYCAP_CON_BLACK;
			break;
		case 1:
			mycapcon->request.connvalues |= MYCAP_CON_WHITE;
			break;
		default:
			break;
		}
		break;
	}

	ip=hhi->GetHeader("X-Forwarded-For");
	logcap << ip <<lend;

	switch(readme(0, ipfile, ip, &cmd)){
		case 1:
			switch(cmd){
				case '1':	//ok to surf
					logcap<<"OK"<<lend;
					if(mycapcon->request.connvalues & MYCAP_CON_BLACK == MYCAP_CON_BLACK){
						mycapcon->request.connvalues |= MYCAP_CON_BLOCK;
					}
					break;
				case '0':	//banned 
					logcap<<"Block"<<lend;
					mycapcon->request.connvalues |= MYCAP_CON_BLOCK;
					break;
				case 'T':	//show turing 
				case 'F':	//show facebook
				case 'G':	//show google
					logcap<<"TFG  "<<cmd<<lend;
					if(findinlist(mymap[cmd], url)!=1){
						logcap<<"BLOCKING --------"<<url<<lend;
						mycapcon->request.connvalues |= MYCAP_CON_BLOCK;
					}
					else{
						logcap<<"PASS --------"<<url<<lend;
					}

					break;
				default: // not on the list
					logcap<<"Default"<<lend;
					break;
			}
			break;
		case 0:
			mycapcon->request.connvalues |= MYCAP_CON_BLOCK;
			break;
		case -1:
			mycapcon->request.connvalues |= MYCAP_CON_BLOCK;
			break;
	}
	//hhi->DisplayHeaders();
	if(mycapcon->request.connvalues & MYCAP_CON_BLOCK){
		if(!(mycapcon->request.connvalues & MYCAP_CON_HTTPS)){
			hhi->SetRequestLine("GET http://www.vtec.net HTTP/1.1");	
		}
		else{

		}
	}
	return CAP_NOCHANGE;
}


/*************************************************************************
 * Request
 *************************************************************************/
CAP_RETURN MyCAP::RequestCAP(void** param1, char** inout, unsigned int* inoutsize, HeaderHandlerInterface* hhi){
	//return CAP_NOCHANGE;
	MyCapConnection* mycapcon = (MyCapConnection*)*param1;

	if(!*param1){
		logd<<"CAP: request null param1 "<<hhi->GetRequestLine()<<lend;
		assert(*param1);
	}
	else{
	}
	return CAP_NOCHANGE;
}


/*************************************************************************
 * Pre-response
 *************************************************************************/
CAP_RETURN MyCAP::PreResponseCAP(void** param1,  HeaderHandlerInterface* hhi){
	//return CAP_NOCHANGE;
	MyCapConnection* mycapcon = (MyCapConnection*)*param1;
	logd<<"CAP: preresponse " << hhi->GetRequestLine()<<lend;
	if(!*param1){
		assert(*param1);
	}
	else{

	}

	if((mycapcon->request.connvalues & MYCAP_CON_BLOCK) && (mycapcon->request.connvalues & MYCAP_CON_HTTPS)){
		printf("whhhhhhhhhhhhhhhhhhhhhhhher er er e er er er er e er er %s\n");
		hhi->SetRequestLine("HTTP/1.1 500 Internal Server Error");
	}
	return CAP_NOCHANGE;
}


/*************************************************************************
 * Response
 *************************************************************************/
CAP_RETURN MyCAP::ResponseCAP(void** param1, char** inout, unsigned int* inoutsize, HeaderHandlerInterface* hhi){
	const char* gzip;
	char* tmps , *p;
	char* out;
	unsigned int outsize;
	int deflateret;
	char tmps64[64];
	MyCapConnection* mycapcon;

	//hhi->DisplayHeaders();
	proxylog.GetAccesslogger();
	if(*param1){
		mycapcon = (MyCapConnection*)*param1;
		if(mycapcon->request.requeston){
			logd<<"CAP: response "<<mycapcon->request.url << "----" << hhi->GetRequestLine() << " " <<lend;
			printf("--------------------%s\n", hhi->GetRequestLine());
			//assert(mycapcon->request.url);
			if(mycapcon->request.url)
				proxylog.ALog("%s - %s\n", mycapcon->request.url, hhi->GetRequestLine() );
			else
				proxylog.ALog("%s - %s\n", "RUBBISH", hhi->GetRequestLine() );
		}
		else{
			logd << "CAP: response null param:request" <<  hhi->GetRequestLine() <<" "<< *inoutsize <<lend;
			assert(0);
		}

		mycapcon->request.requeston=0;

		gzip = hhi->GetHeader("Content-Encoding");
		if(gzip && strcmp(gzip, "gzip")==0){
			logd<<"CAP:GZ: Content gzip encoded"<<lend;
			out = (char*)malloc(5000000);
			outsize=5000000;
			if((deflateret=http_inflate(*inout, inoutsize, out, &outsize, 0))==0){
				hhi->RemoveHeader("Content-Encoding");
				logd<<"CAP:GZ: Inflate succeeded "<<outsize<<lend;
				logd<<"CAP:GZ: XXX "<<*inoutsize<<" -> "<<outsize<<"  "<<outsize/(*inoutsize)<<"  "<< hhi->GetHeader("Content-type")<<lend;
				tmps = (char*)realloc(*inout, sizeof(char)*outsize);
				memcpy(tmps,out, outsize);
				*inout=tmps;
				*inoutsize=outsize;
				snprintf(tmps64, 64-1, "%d", *inoutsize); 
				hhi->ReplaceHeader("Content-Length", tmps64);
				//printchararray(inout, inoutsize, "))))))))))))))))");
			}
			else{
				if(deflateret=Z_BUF_ERROR)
					logd<<"CAP:GZ: Inflate failed (mem)"<<*inoutsize<<" -> "<<outsize<<"  "<<outsize/(*inoutsize)<<"  "<< hhi->GetHeader("Content-type")<<lend;
				else
					logd<<"CAP:GZ: Inflate failed "<<deflateret<<" "<<hhi->GetHeader("Content-type")<<lend;
			}
			free(out);
		}
		if(*inoutsize && hhi->GetHeader("content-type")){
		       	if(strstr(hhi->GetHeader("content-type"), "text/html")){
				if(strnistrn(*inout, *inoutsize, "<html>",6)){
					const char* into="Powered by the prox";
					
					if(!(tmps=(char*)realloc(*inout,*inoutsize+strlen(into)))){
						logc<<"CAP: Critical mem error "<<(*inoutsize+strlen(into))<<lend;
						return CAP_ERROR;
					}
					else{
						p=strnistrn(tmps, *inoutsize, "<html>",6);
						memmove(p+6+strlen(into), p+6, *inoutsize-((p+6)-tmps));
						memcpy(p+6, into, strlen(into));
						*inoutsize+=strlen(into);
						snprintf(tmps64, 64-1, "%d", *inoutsize); 
						hhi->ReplaceHeader("Content-Length", tmps64);
						*inout=tmps;
						return CAP_MODIFIED;
					}
				}
			}
		}

	}
	else{
		logd << "CAP: response null param" <<  hhi->GetRequestLine() <<" "<< *inoutsize <<lend;
		assert(*param1);
	}


	return CAP_NOCHANGE;
}


/*************************************************************************
 * Init
 *************************************************************************/
CAP_RETURN MyCAP::InitCAP(){
	InitParameters* sections, * initp;
	char tmps8[8];
	int i;
	char* c;
	mycaplist::iterator iter;

	initp = new InitParameters();
	if(initp->ReadInitFile("mycap.ini")==-1){
		loge<<"MYCAP: Could not load default preproxy.ini file"<<lend;
	}

	initp->DisplayInitParameters();

	ipfile=strdup(initp->GetInitParameter("ipfile"));

	if((sections=initp->GetSectionByName("White-Domains"))){
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			hosts.push_back(std::make_pair(strdup(c), 1));	
			printf("1-- %s\n", c);
		}
	}

	if((sections=initp->GetSectionByName("Black-Domains"))){
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			hosts.push_back(std::make_pair(strdup(c), 0));	
			printf("2-- %s\n", c);
		}
	}

	if((sections=initp->GetSectionByName("Black-Urls"))){
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			urls.push_back(std::make_pair(strdup(c), 0));	
			printf("3-- %s\n", c);
		}
	}

	if((sections=initp->GetSectionByName("White-Urls"))){
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			urls.push_back(std::make_pair(strdup(c), 1));	
			printf("4-- %s\n", c);
		}
	}

	if((sections=initp->GetSectionByName("G"))){
		mymap['G']=new mycaplist;
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			mymap['G']->push_back(std::make_pair(strdup(c), 1));	
			printf("4-- %s\n", c);
		}
	}
	if((sections=initp->GetSectionByName("T"))){
		mymap['T']=new mycaplist;
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			mymap['T']->push_back(std::make_pair(strdup(c), 1));	
			printf("4-- %s\n", c);
		}
	}
	if((sections=initp->GetSectionByName("F"))){
		mymap['F']=new mycaplist;
		for(i=0; (sprintf(tmps8,"%d",i))&&(c = (char*)sections->GetInitParameter(tmps8)) ; i++){
			mymap['F']->push_back(std::make_pair(strdup(c), 1));	
			printf("4-- %s\n", c);
		}
	}





	delete initp;

	return CAP_NOCHANGE;
}


short MyCAP::findinlist(mycaplist* m, const char* c){
	mycaplist::iterator iter;		
	for (iter = m->begin(); iter != m->end(); iter++) {
		printf("=%s=====%s=  %d\n", c, iter->first, iter->second);
		if(strstr(c, iter->first)){
			return iter->second;
		}
	}
	return -1;
}

/*************************************************************************
 * Clear 
 *************************************************************************/
CAP_RETURN MyCAP::ClearCAP(){
	int i;
	mycaplist::iterator iter;		
	mycapmap::iterator iterx;

	if(ipfile)
		free(ipfile);

	for (iter = urls.begin(); iter != urls.end(); iter++) {
		free((char*)iter->first);
	}
	urls.clear();

	for (iter = hosts.begin(); iter != hosts.end(); iter++) {
		free((char*)iter->first);
	}
	hosts.clear();

	for( iterx = mymap.begin(); iterx != mymap.end(); iterx++){
		for(iter = (iterx->second)->begin(); iter != (iterx->second)->end(); iter++){
			free((char*)iter->first);
		}
		delete iterx->second ;
	}
	mymap.clear();

	return CAP_NOCHANGE;
}





short readme(FILE** xx, const char* fname, const char* ip, char* cmd){
	long fsize;
	char* buffer;
	short retval= -1;
	FILE* f;
	char tmps32[32];
	struct flock lock;
	char* c;
	
	lock.l_type    = F_RDLCK; 
	lock.l_start   = 0;
	lock.l_whence  = SEEK_SET;
	lock.l_len     = 0;        

	*cmd='0';	
	if(!(f=fopen(fname, "r")))
		return -1;

	if(fcntl(fileno(f),F_SETLK, &lock )!=-1){
		fseek(f, 0L , SEEK_END);
		fsize = ftell(f);
		rewind(f);
		if((buffer = (char*)calloc(fsize+1, 1))){
			if(fread(buffer , fsize, 1 , f)){
				snprintf(tmps32, 32, "|%s|", ip);
				if((c=strstr(buffer, tmps32))){
					if(c-buffer > 0){
						c--;
						printf("####\n%s\n%s\n##%c#\n", buffer, tmps32, *c);
						*cmd=*c;
					}
					retval=1;
				}
				else
					retval=0;	
			}
			free(buffer);
		}
		lock.l_type = F_UNLCK;
		fcntl(fileno(f), F_SETLK, &lock);	
	}
	fclose(f);
	return retval;
}




